﻿
#include <stdio.h>
#include <thread>
#include <cstring>

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/videoio/videoio_c.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/mat.hpp"

#include "QttVideo.h"
#include "QttChannel.h"
#include "json/CJsonObject.hpp"

class WinVideoCaptuer : public Qtt::VideoCapturer {
private:
	int mWidth;
	int mHeight;
	int mFps;
	int mBps;
	Qtt::FrameFormat mFormat;

	bool mIsStarted;
	std::thread* mCaptureThread;
	cv::VideoCapture* mCap;
public:
	WinVideoCaptuer(int width, int height, int fps, int bps, Qtt::FrameFormat format) {
		mWidth = width;
		mHeight = height;
		mFps = fps;
		mBps = bps;
		mFormat = format;
		mIsStarted = false;
		mCaptureThread = nullptr;
		mCap = nullptr;
	}

	virtual ~WinVideoCaptuer() {
		onStop();
	}

	int width() override {
		return mWidth;
	}

	int height() override {
		return mHeight;
	}

	int fps() override {
		return mFps;
	}

	int bps() override {
		return mBps;
	}

	Qtt::FrameFormat captureFormat() override {
		return mFormat;
	}

	int onStart() override {
		if (mIsStarted)
			return 0;

		mCap = new cv::VideoCapture(0);
		if (!mCap->isOpened()) {
			printf("camera can't be captured\n");
			return 1;
		}
		mIsStarted = true;
		//start capture thread
		mCaptureThread = new std::thread(&WinVideoCaptuer::videoCaptureFunc, this);

		return 0;
	}

	void onStop() override {
		mIsStarted = false;
		if (mCap) {
			delete mCap;
			mCap = nullptr;
		}
		if (mCaptureThread) {
			mCaptureThread->join();
			delete mCaptureThread;
			mCaptureThread = nullptr;
		}
	}

private:
	template<typename _Tp>
	std::vector<_Tp> convertMat2Vector(const cv::Mat& mat) {
		return (std::vector<_Tp>) (mat.reshape(1, 1));//通道数不变，按行转为一行
	}
	void videoCaptureFunc() {
		printf("Video Capture thread is running\n");
		while (mIsStarted) {
			cv::Mat cvFrame;
			if (!mCap->read(cvFrame)) {
				printf("capture frame failed\n");
				continue;
			}
			
			std::vector<uint8_t> vec = convertMat2Vector<uint8_t>(cvFrame);
			putFrame(vec.data(), vec.size()); //把捕获的帧传入Channel
			cv::imshow("me", cvFrame);
			cv::waitKey(10);
			std::this_thread::sleep_for(std::chrono::milliseconds(20));
		}
		printf("Video Capture thread is ending\n");
	}
};

class WinVideoDisplayer : public Qtt::VideoDisplayer {
public:
	std::string name;
	WinVideoDisplayer() {
		name = "";
	}

	int onStart(uint32_t displayId, int width, int height) override {
		static int count = 1;
		std::stringstream ss;
		ss << "win" << count++;
		name = ss.str();
		return 0; //0是成功
	}

	void onDisplay(Qtt::VideoFrame* frame, uint32_t displayId) override {
		cv::Mat cvMat;
		cvMat.create(cv::Size(frame->width, frame->height), CV_8UC3);
		uint8_t* data = frame->data;
		int data_len = frame->data_len;
		memcpy(cvMat.data, data, data_len);
		imshow(name, cvMat);
		cv::waitKey(10);
		cvMat.release();
	}

	void onStop(uint32_t displayId) override {
		cv::destroyWindow(name);
	}
};

/**
 * 所有操作都是异步操作
 */
class ChannelObserverImpl : public Qtt::ChannelObserver {
public:
	void onJoinSuccess(uint32_t roomId, uint32_t uid, Qtt::ChannelRole role) override {
		printf("自己加入房间成功，角色是:%d\n", role);
		//todo 这里实现加入房间成功的逻辑

	}
	void onJoinFail(int code, const char* message) override {
		printf("自己加入房间失败，失败码:%d, 信息=%s\n", code, message);
		//todo 这里实现加入房间失败的逻辑
	}
	bool onWaitReconnecting(int seconds) override {
		printf("%d秒后重连:\n", seconds);
		return true; //重连返回true，取消重连返回false
	}
	void onOtherJoin(Qtt::ChannelUser* user) override {
		printf("uid:%u 用户加入房间，视频=%d\n", user->uid, user->enableVideo);
		//todo 这里实现别人进入房间的逻辑
	}
	void onOtherLeave(Qtt::ChannelUser* user) override {
		printf("uid:%u 离开房间\n", user->uid);
		//todo 这里实现别人离开房间的逻辑
	}
	void onOtherMuted(Qtt::ChannelUser* user) override {
		printf("uid:%u 静音状态改变，静音:%d\n", user->uid, user->muted);
		//todo 这里实现别人静音状态改变的逻辑
	}
	void onOtherRoleChanged(Qtt::ChannelUser* user) override {
		printf("uid:%u 连麦角色变化，角色是:%d\n", user->uid, user->audioRole);
		//todo 这里实现别人连麦角色状态改变的逻辑
	}
	void onSelfRoleChanged(Qtt::ChannelRole role) override {
		printf("自己改变角色成功，角色是:%d\n", role);
		//todo 这里实现自己连麦角色状态改变的逻辑
	}
	void onSelfTalking(int talking) override {
		printf("自己讲话状态，音量为:%d\n", talking);
		//todo 这里实现自己说话音量状态改变的逻辑
	}
	void onOtherTalking(Qtt::ChannelUser* user, int talking) override {
		printf("uid:%u 说话状态改变，音量为:%d\n", user->uid, talking);
		//todo 这里实现他人说话音量状态改变的逻辑
	}
	void onOtherNetworkStats(Qtt::ChannelUser* user) override {
		printf("uid:%u 用户网络情况: Bps=%d, 抖动=%d, 丢包率=%d, 端到端时延=%dms\n",
			user->uid, user->curbps, user->jitter, user->lossRate, user->distance);
		//todo 这里实现他人网络情况统计的逻辑
	}
	void onSelfNetworkStats(int rtt) override {
		printf("自己网络情况rtt=%dms\n", rtt);
		//todo 这里实现自己网络情况统计的逻辑
	}
	void onClose() override {
		printf("网络断开\n");
		//todo 这里实现退出房间的逻辑
	}
};

int main(int argc, char* argv[]) {
	std::string cfg_file = ".\\wincfg.txt";
	if (argc == 2)
		cfg_file = argv[1];

	FILE* fp = NULL;
	if ((fp = fopen(cfg_file.c_str(), "r")) == NULL) {
		printf("Fail to open configure file!\n");
		return -1;
	}

	char buffer[200];
	fread(buffer, 200, 1, fp);
	qtt::CJsonObject repJson;
	if (!repJson.Parse(buffer)) {
		printf("json Parse failed\n");
		return -1;
	}

	std::string server, role;
	int port, room, fps, bitrate, cap_width, cap_height;
	repJson.Get("server", server);
	repJson.Get("port", port);
	repJson.Get("room", room);
	repJson.Get("role", role);
	repJson.Get("fps", fps);
	repJson.Get("bitrate", bitrate);
	repJson.Get("cap_width", cap_width);
	repJson.Get("cap_height", cap_height);

	printf("server: %s, port: %d, room: %d, role: %s, fps: %d, bitrate: %d, cap_width: %d, cap_height: %d\n", 
		server.c_str(), port, room, role.c_str(), fps, bitrate, cap_width, cap_height);

	Qtt::ChannelFactory::SetAppkey("18140105956");
	Qtt::ChannelEngine* channel = Qtt::ChannelFactory::GetChannelInstance();
	if (channel == NULL) {
		printf("GetQttAudioChannel failed, reason=%s\n", Qtt::ChannelFactory::GetError());
		return -1;
	}
	else
		printf("GetQttAudioChannel success\n");

	ChannelObserverImpl* listener = new ChannelObserverImpl();
	srand((unsigned)time(NULL));
	uint32_t uuid = rand() % 10000;
	printf("my uid=%u\n", uuid);
	channel->setUid(uuid);
	channel->setServer(server.c_str(), port, "");
	channel->setObserver(listener);
	WinVideoCaptuer* winVideoCaptuer = new WinVideoCaptuer(cap_width, cap_height, fps, bitrate, Qtt::BGR24);
	WinVideoDisplayer* winVideoDisplayer = new WinVideoDisplayer;
	Qtt::VideoPlugin::SetVideoCard(winVideoCaptuer, winVideoDisplayer, Qtt::BGR24);

	if (role == "TALKER") {
		channel->join(room, Qtt::TALKER);
	}
	else {
		channel->join(room, Qtt::AUDIENCE);
	}

	getchar();

	delete channel;
	delete listener;
	delete winVideoDisplayer;
	delete winVideoCaptuer;

	return 0;
}
